package com.ruoyi.business.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.bizsys.domain.ExtMonnifyReservedAccounts;
import com.ruoyi.bizsys.domain.ExtTryQueue;
import com.ruoyi.bizsys.domain.RealNameAuth;
import com.ruoyi.bizsys.service.IExtMonnifyReservedAccountsService;
import com.ruoyi.bizsys.utils.BizConstants;
import com.ruoyi.bizsys.vo.MonnifyReservedAccount;
import com.ruoyi.business.common.AsyncService;
import com.ruoyi.business.service.IMonnifyService;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.enums.RspEnmu;
import com.ruoyi.common.exception.BusinessException;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.extdata.common.RetryFunction;
import com.ruoyi.extdata.common.config.MonnifyConfig;
import com.ruoyi.extdata.service.IExternalApiService;
import com.ruoyi.system.service.ISysDictTypeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author: jamnsin
 * @Date: 2022/5/19 14:44
 */
@Slf4j
@Service
public class MonnifyServiceImpl extends BaseService implements IMonnifyService {

    @Autowired
    private IExtMonnifyReservedAccountsService extMonnifyReservedAccountsService;

    @Autowired
    @Lazy
    private AsyncService asyncService;

    @Autowired
    RetryFunction retryFunction;

    @Autowired
    IExternalApiService externalApiService;

    @Autowired
    MonnifyConfig monnifyConfig;

    @Autowired
    private ISysDictTypeService dictTypeService;

    public static final String MONNIFY_ACCOUNT_SUPPORT_BANK = "monnify_account_support_bank";

//    @Override
//    public MonnifyReservedAccount getMonnifyReservedAccounts(String custNo) {
//        return custVaService.getVaReservedAccount(custNo);

//
//        MonnifyReservedAccount monnifyReservedAccount = null;
//
//        // 查询所有monnify账户支持银行
//        List<String> monnifySupportBanks = getMonnifySuppertBanks();
//
//        // 查询monnify虚拟账户
//        List<ExtMonnifyReservedAccounts> monnifyReservedAccounts = extMonnifyReservedAccountsService.list(new QueryWrapper<ExtMonnifyReservedAccounts>().eq("cust_no", custNo).eq("state", UserConstants.BUSINESS_NORMAL).select("cust_no", "bvn", "account_reference", "accounts_account_name", "accounts_account_number", "accounts_bank_name", "accounts_bank_code", "created_on").last("order by id desc"));
//        if (monnifyReservedAccounts.size() == 0) {
//            // 生成默认银行账户
//            monnifyReservedAccount = generateMonnifyReservedAccount(custNo);
//            asyncGenerateMonnifyReservedAccount(custNo, getOtherMonnifyAccountSupportBank(monnifySupportBanks));
//            return monnifyReservedAccount;
//        }
//
//        log.info("当前已有虚拟账户：{}", monnifyReservedAccounts);
//
//        // 从 monnifyReservedAccounts 中取出默认银行编码记录
//        ExtMonnifyReservedAccounts reservedAccounts = monnifyReservedAccounts.stream().filter(p -> p.getAccountsBankCode().equals(getDefaultBankCode())).findFirst().orElse(null);
//        if (reservedAccounts == null) {
//            // 生成默认银行账户
//            monnifyReservedAccount = generateMonnifyReservedAccount(custNo);
//
//            asyncGenerateMonnifyReservedAccount(custNo, getOtherMonnifyAccountSupportBank(monnifySupportBanks, getCurrentMonnifyBanks(monnifyReservedAccounts, getDefaultBankCode())));
//            return monnifyReservedAccount;
//        }
//
//        asyncGenerateMonnifyReservedAccount(custNo, getOtherMonnifyAccountSupportBank(monnifySupportBanks, getCurrentMonnifyBanks(monnifyReservedAccounts)));
//        return getMonnifyReservedAccount(reservedAccounts);
//    }

    private List<String> getMonnifySuppertBanks() {
        List<SysDictData> monnifySuppertBanks = DictUtils.getDictCache(BizConstants.DICT_TYPE_VA_MONNIFY);
        if (CollectionUtils.isEmpty(monnifySuppertBanks)) {
            monnifySuppertBanks = dictTypeService.selectDictDataByType(BizConstants.DICT_TYPE_VA_MONNIFY);
            DictUtils.setDictCache(BizConstants.DICT_TYPE_VA_MONNIFY, monnifySuppertBanks);
        }
        log.info("Monnify 虚拟账户支持银行：{}", monnifySuppertBanks);
        return monnifySuppertBanks.stream().map(SysDictData::getDictValue).collect(Collectors.toList());
    }

    @Override
    public List<MonnifyReservedAccount> getAllMonnifyReservedAccounts(String custNo) {
        List<MonnifyReservedAccount> monnifyReservedAccounts = new ArrayList<>();

        // 查询所有monnify账户支持银行
        List<String> monnifySupportBanks = getMonnifySuppertBanks();

        // 查询monnify虚拟账户
        List<ExtMonnifyReservedAccounts> extMonnifyReservedAccounts = extMonnifyReservedAccountsService.list(new QueryWrapper<ExtMonnifyReservedAccounts>().eq("cust_no", custNo).eq("state", UserConstants.BUSINESS_NORMAL).select("cust_no", "bvn", "account_reference", "accounts_account_name", "accounts_account_number", "accounts_bank_name", "accounts_bank_code", "created_on").last("order by id desc"));
        if (extMonnifyReservedAccounts.size() == 0) {
            monnifySupportBanks.stream().forEach(bankCode -> {
                try {
                    MonnifyReservedAccount monnifyReservedAccount = generateMonnifyReservedAccount(custNo, bankCode);
                    monnifyReservedAccounts.add(monnifyReservedAccount);
                } catch (Exception e) {
                    if (e instanceof BusinessException) {
                        log.error("生成 monnify 虚拟账户失败，bankCode = {}, 错误信息：", bankCode, e.getMessage());
                    } else {
                        log.error("生成 monnify 虚拟账户异常，bankCode = {}, 错误信息：", bankCode, e);
                    }
                }
            });
        } else {
            addAll(monnifyReservedAccounts, extMonnifyReservedAccounts);

            List<String> otherMonnifyBanks = getOtherMonnifyAccountSupportBank(monnifySupportBanks, getCurrentMonnifyBanks(extMonnifyReservedAccounts));
            otherMonnifyBanks.stream().distinct().forEach(bankCode -> {
                try {
                    MonnifyReservedAccount monnifyReservedAccount = generateMonnifyReservedAccount(custNo, bankCode);
                    monnifyReservedAccounts.add(monnifyReservedAccount);
                } catch (Exception e) {
                    if (e instanceof BusinessException) {
                        log.error("生成 monnify 虚拟账户失败，bankCode = {}, 错误信息：", bankCode, e.getMessage());
                    } else {
                        log.error("生成 monnify 虚拟账户异常，bankCode = {}, 错误信息：", bankCode, e);
                    }
                }
            });
        }

        if (CollectionUtils.isEmpty(monnifyReservedAccounts)) {
            log.error("生成 monnify 虚拟账户数量为 0");
            throw new BusinessException(RspEnmu.NETWORK_BUSY);
        }

        return monnifyReservedAccounts;
    }

    private void addAll(List<MonnifyReservedAccount> monnifyReservedAccounts, List<ExtMonnifyReservedAccounts> extMonnifyReservedAccounts) {
        extMonnifyReservedAccounts.stream().forEach(extMonnifyReservedAccount -> {
            MonnifyReservedAccount monnifyReservedAccount = getMonnifyReservedAccount(extMonnifyReservedAccount);
            monnifyReservedAccounts.add(monnifyReservedAccount);
        });
    }

    private List<String> getCurrentMonnifyBanks(List<ExtMonnifyReservedAccounts> monnifyReservedAccounts) {
        return monnifyReservedAccounts.stream().map(ExtMonnifyReservedAccounts::getAccountsBankCode).collect(Collectors.toList());
    }

    private List<String> getCurrentMonnifyBanks(List<ExtMonnifyReservedAccounts> monnifyReservedAccounts, String bankCode) {
        List<String> currentMonnifyBanks = monnifyReservedAccounts.stream().map(ExtMonnifyReservedAccounts::getAccountsBankCode).collect(Collectors.toList());
        currentMonnifyBanks.add(bankCode);
        log.info("当前现有虚拟账户：{}", currentMonnifyBanks);
        return currentMonnifyBanks;
    }

    private void asyncGenerateMonnifyReservedAccount(String custNo, List<String> monnifySupportBanks) {
        if (monnifySupportBanks.size() == 0) {
            return;
        }

        // monnifySupportBanks 去重遍历
        monnifySupportBanks.stream().distinct().forEach(bankCode -> {
            asyncService.asyncGenerateMonnifyReservedAccount(custNo, bankCode);
        });
    }

    private List<String> getOtherMonnifyAccountSupportBank(List<String> monnifySupportBanks) {
        return monnifySupportBanks.stream().filter(p -> !p.equals(getDefaultBankCode())).collect(Collectors.toList());
    }

    private List<String> getOtherMonnifyAccountSupportBank(List<String> monnifySupportBanks, List<String> currentMonnifyBanks) {
        // 获取monnify账户支持银行与已存在的银行编码的差集
        List<String> otherMonnifyBanks = monnifySupportBanks.stream().filter(p -> !currentMonnifyBanks.contains(p)).collect(Collectors.toList());
        log.info("待生成预留账户的银行编码：{}", otherMonnifyBanks);
        return otherMonnifyBanks;
    }

    @Override
    public MonnifyReservedAccount generateMonnifyReservedAccount(String custNo) {
        String defaultBankCode = getDefaultBankCode();
        log.info("准备生成默认银行 {} 虚拟账户", defaultBankCode);
        return generateMonnifyReservedAccount(custNo, defaultBankCode);
    }

    private String getDefaultBankCode() {
        String bankCode = null;
        try {
            bankCode = DictUtils.getDefaultDictValue(MONNIFY_ACCOUNT_SUPPORT_BANK);
            if (StringUtils.isBlank(bankCode)) {
                bankCode = monnifyConfig.getBankCard();
            }
        } catch (Exception e) {
            log.error("获取默认银行编码失败，改为取配置中的默认银行 {}。错误信息：", monnifyConfig.getBankCard(), e);
            bankCode = monnifyConfig.getBankCard();
        }
        return bankCode;
    }

    @Override
    public MonnifyReservedAccount generateMonnifyReservedAccount(String custNo, String bankCode) {

        String accountReference = IdUtils.generateCustNo();
        log.debug("准备生成 monnify 虚拟账户，custNo：{}, bankCode：{}, accountReference：{}", custNo, bankCode, accountReference);

        JSONObject rspJson = null;
        try {
            JSONObject reqJson = buildMonnifyReservedAccountReqData(custNo, accountReference, bankCode);
            rspJson = (JSONObject) retryFunction.tryCount(e -> externalApiService.callMonnifyService(reqJson, monnifyConfig.getBankTransferReservedAccounts(), "Monnify-Reserved-Accounts", true), custNo, ExtTryQueue.VIRTUAL);
            return getMonnifyReservedAccount(saveMonnifyReservedAccount(custNo, rspJson));
        } catch (BusinessException e) {
            log.error("生成客户 Monnify VA 异常，code = {}, msg = {}", e.getCode(), e.getMessage());

            if (RspEnmu.MONNIFY_RESERVED_IS_EXISTS.getCode().equals(e.getCode())) {
                rspJson = externalApiService.callMonnifyService(new JSONObject(), monnifyConfig.getGetBankTransferReservedAccounts() + accountReference, "Monnify-Get-Reserved-Accounts", false);
                return getMonnifyReservedAccount(saveMonnifyReservedAccount(custNo, rspJson));
            }

            if (RspEnmu.MONNIFY_RESERVED_DO_NOT_SUPPORT.getCode().equals(e.getCode())) {
                log.warn("客户号 {} 生成预留账户失败，银行编码 {} 不支持", custNo, bankCode);
            }

            throw e;
        }
    }

    private JSONObject buildMonnifyReservedAccountReqData(String custNo, String accountReference, String bankCode) {
        RealNameAuth realNameAuth = getRealName(custNo);
        if (realNameAuth == null) {
            throw new BusinessException(RspEnmu.CUSTOMER_DELETE_ACCOUNT);
        }

        JSONObject reqJson = new JSONObject();
        reqJson.put("accountReference", accountReference);
        reqJson.put("accountName", realNameAuth.getFirstName() + "-LinkCredit/Monnify");
        reqJson.put("currencyCode", "NGN");
        reqJson.put("contractCode", monnifyConfig.getContractCode());
        reqJson.put("customerEmail", realNameAuth.getUserId().substring(realNameAuth.getUserId().length() - 10) + "@LCserver.com");
        reqJson.put("bvn", realNameAuth.getBvnAccount());
        reqJson.put("customerName", realNameAuth.getFullName());
        reqJson.put("getAllAvailableBanks", false);
        reqJson.put("preferredBanks", new String[]{bankCode});

        if (StringUtils.isBlank(bankCode)) {
            List<String> monnifySupportBanks = getMonnifySuppertBanks();
            reqJson.put("preferredBanks", monnifySupportBanks.toArray());
        }

        return reqJson;
    }

    private RealNameAuth getRealName(String custNo) {
        QueryWrapper<RealNameAuth> realNameAuthQueryWrapper = new QueryWrapper<>();
        realNameAuthQueryWrapper.eq("cust_no", custNo).eq("state", UserConstants.BUSINESS_NORMAL).select("user_id", "bvn_account", "first_name", "middle_name", "last_name");
        RealNameAuth realNameAuth = realNameAuthService.getOne(realNameAuthQueryWrapper);
        return realNameAuth;
    }

    private ExtMonnifyReservedAccounts saveMonnifyReservedAccount(String custNo, JSONObject responseBody) {
        ExtMonnifyReservedAccounts monnifyReservedAccount = new ExtMonnifyReservedAccounts();
        monnifyReservedAccount.setUsrId(getUserIdByCustNo(custNo));
        monnifyReservedAccount.setCustNo(custNo);
        monnifyReservedAccount.setContractCode(responseBody.getString(("contractCode")));
        monnifyReservedAccount.setAccountReference(responseBody.getString(("accountReference")));
        monnifyReservedAccount.setAccountName(responseBody.getString(("accountName")));
        monnifyReservedAccount.setCurrencyCode(responseBody.getString(("currencyCode")));
        monnifyReservedAccount.setCustomerEmail(responseBody.getString(("customerEmail")));
        monnifyReservedAccount.setCustomerName(responseBody.getString(("customerName")));

        String bankCode = null;
        if (responseBody.getJSONArray("accounts") != null && responseBody.getJSONArray("accounts").size() > 0) {
            JSONObject accounts = JSONObject.parseObject(responseBody.getJSONArray("accounts").get(0).toString());
            bankCode = accounts.getString("bankCode");
            monnifyReservedAccount.setAccountsBankCode(bankCode);
            monnifyReservedAccount.setAccountsBankName(accounts.getString("bankName"));
            monnifyReservedAccount.setAccountsAccountNumber(accounts.getString("accountNumber"));
            monnifyReservedAccount.setAccountsAccountName(accounts.getString("accountName"));
        }

        monnifyReservedAccount.setCollectionChannel(responseBody.getString(("collectionChannel")));
        monnifyReservedAccount.setReservationReference(responseBody.getString(("reservationReference")));
        monnifyReservedAccount.setReservedAccountType(responseBody.getString(("reservedAccountType")));
        monnifyReservedAccount.setStatus(responseBody.getString(("status")));
        monnifyReservedAccount.setCreatedOn(responseBody.getString(("createdOn")));
        monnifyReservedAccount.setBvn(responseBody.getString(("bvn")));
        monnifyReservedAccount.setRestrictPaymentSource(responseBody.getString(("restrictPaymentSource")));

        QueryWrapper<ExtMonnifyReservedAccounts> monnifyReservedAccountsQueryWrapper = new QueryWrapper<>();
        monnifyReservedAccountsQueryWrapper.eq("cust_no", custNo).eq("accounts_bank_code", bankCode).eq("state", UserConstants.BUSINESS_NORMAL).select("id", "cust_no").last("limit 1");
        ExtMonnifyReservedAccounts extMonnifyReservedAccounts = extMonnifyReservedAccountsService.getOne(monnifyReservedAccountsQueryWrapper);
        if (extMonnifyReservedAccounts != null && StringUtils.isNotBlank(bankCode)) {
//            monnifyReservedAccount.setId(extMonnifyReservedAccounts.getId());
//            extMonnifyReservedAccountsService.updateExtMonnifyReservedAccounts(monnifyReservedAccount);
        } else {
            extMonnifyReservedAccountsService.insertExtMonnifyReservedAccounts(monnifyReservedAccount);
        }

        return monnifyReservedAccount;
    }

    private MonnifyReservedAccount getMonnifyReservedAccount(ExtMonnifyReservedAccounts reservedAccount) {
        MonnifyReservedAccount monnifyReservedAccount = new MonnifyReservedAccount();
        monnifyReservedAccount.setCustNo(reservedAccount.getCustNo());
        monnifyReservedAccount.setBvn(reservedAccount.getBvn());
        monnifyReservedAccount.setAccountReference(reservedAccount.getAccountReference());
        monnifyReservedAccount.setAccountNumber(reservedAccount.getAccountsAccountNumber());
        monnifyReservedAccount.setAccountName(reservedAccount.getAccountsAccountName());
        monnifyReservedAccount.setBankName(reservedAccount.getAccountsBankName());
        monnifyReservedAccount.setBankCode(reservedAccount.getAccountsBankCode());
        monnifyReservedAccount.setCreatedOn(reservedAccount.getCreatedOn());
        return monnifyReservedAccount;
    }

}
